﻿using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using Ini;
using Microsoft.Win32;

namespace Func {

  public class UserProcess {
    private const int SLEEP_AMOUNT = 200;
    // объект пользовательского процесса
    private Process Proc = new Process();
    // текущее время ожидания завершения пользовательского процесса
    private int ElapsedTime;
    // флаг завершения пользовательского процесса
    private bool EventHandled;
    // обработчик завершения процесса
    private EventHandler EventExit;

    // получить объект процесса
    public Process ObjProc {
      get {
        return Proc;
      }
    }

    // установить обработчик завершения процесса
    public EventHandler OnExit {
      set {
        EventExit = value;
      }
    }

    //*******************************
    //* Function: QuotesStr
    //* Notes: Oбрамление строки в двойные кавычки
    //*
    public string QuotesStr (string Str) {
      return !string.IsNullOrWhiteSpace(Str) ?
          Str.Contains(" ") && (!Str.StartsWith("\"") && !Str.EndsWith("\"")) ?
              "\"" + Str + "\"" : Str :
              string.Empty;
    }

    //*** End of QuotesStr ***********

    //*******************************
    //* Function: DequotesStr
    //* Notes: Удаление обрамления строки в двойных кавычках
    //*
    public string DequotesStr (string Str) {
      if (Str.StartsWith("\"")) {
        Str = Str.Substring(Str.IndexOf("\"") + 1);
      }
      if (Str.EndsWith("\"")) {
        Str = Str.Substring(0, Str.Length - 1);
      }
      return Str;
    }

    //*** End of DequotesStr ***********

    //*******************************
    //* Function: ParseStr
    //* Notes: Парсинг строки, выделение параметров и помещение их
    //         в строковый массив 
    //*
    public string[] ParseStr (string Str) {
      // начальная позиция параметра в строке
      int BegPos = 0;
      // конечная позиция параметра в строке
      int EndPos = 0;
      // длина строки параметров
      int Len = 0;
      // текущий символ
      char Symb;
      // флаг параметра
      bool FlagPar = false;
      // флаг обрамления строки в двойные кавычки
      bool FlagQuotes = false;
      // флаг удаления символов обрамления
      bool FlagDel = false;
      // текущий параметр
      string Par = "";
      // массив параметров
      ArrayList ArrPar = new ArrayList();

      // определяем длину строки
      Len = Str.Length;
      for (int i = 0; i < Len; i++) {
        Symb = Str[i];
        // текущий символ пробел
        if (Symb == ' ') {
          if (FlagPar) {
            if (FlagQuotes) {
              if (i < Len)
                continue;
            }
            FlagPar = false;
            EndPos = i;
            // выделяем текущий параметр
            Par = Str.Substring(BegPos, EndPos - BegPos);
            if (Par[0] == '\"') {
              if (Par[Par.Length - 1] == '\"') {
                FlagDel = true;
              }
              else {
                string Err = "Ошибка в строке: " + Str + "\n";
                Err = Err + "В конце параметра: " + Par +
                  " - нет завершающей двойной кавычки!";
                throw new Exception(Err);
              }
            }
            // удаляем из параметра символы обрамления
            if (FlagDel) {
              FlagDel = false;
              FlagQuotes = false;
              Par = DequotesStr(Par);
            }
            // помещаем в массив параметров
            ArrPar.Add(Par);
          }
        }
        // текущий символ - не пробел;
        else {
          if (!FlagPar) {
            FlagPar = true;
            BegPos = i;
            if (Str[i] == '\"')
              FlagQuotes = true;
          }
          else {
            if (i + 1 == Len) {
              EndPos = Len;
              // выделяем текущий параметр
              Par = Str.Substring(BegPos, EndPos - BegPos);
              if (Par[0] == '\"') {
                if (Par[Par.Length - 1] == '\"') {
                  FlagDel = true;
                }
                else {
                  string Err = "Ошибка в строке: " + Str + "\n";
                  Err = Err + "В конце параметра: " + Par +
                    " - нет завершающей двойной кавычки!";
                  throw new Exception(Err);
                }
              }
              // удаляем из параметра символы обрамления
              if (FlagDel) {
                FlagDel = false;
                FlagQuotes = false;
                Par = DequotesStr(Par);
              }
              // помещаем в массив параметров
              ArrPar.Add(Par);
            }
            else {
              if (Str[i] == '\"' && FlagQuotes)
                FlagQuotes = false;
            }
          }
        }
      }
      // создаем возвращаемый строковый массив параметров
      string[] Arr = new string[ArrPar.Count];
      ArrPar.CopyTo(Arr);
      return Arr;
    }

    //*** End of ParseStr ***********

    //*******************************
    //* Function: Run
    //* Notes: Запуск пользовательского процесса
    //*

    public bool Run (string FileName, string Arg, string WorkDir,
                     bool WaitExit, int TimeWaitExit) {
      // текущее время ожидание завершения процесса
      ElapsedTime = 0;
      // флаг завершения процесса
      EventHandled = false;

      try {
        // устанавливаем имя запускаемого приложения
        Proc.StartInfo.FileName = FileName;
        // устанавливаем аргументы командной строки
        if ((Arg != null) && (Arg != ""))
          Proc.StartInfo.Arguments = Arg;
        // устанавливаем рабочий каталог
        if ((WorkDir != null) && (WorkDir != ""))
          Proc.StartInfo.WorkingDirectory = WorkDir;
        // устананавливаем время ожидания завершения приложения
        // и устанавливаем обработчик завершения приложения
        if (WaitExit) {
          Proc.EnableRaisingEvents = true;
          if (TimeWaitExit == 0)
            TimeWaitExit = 30000;
          else
            TimeWaitExit = TimeWaitExit * 1000;
          Proc.Exited += new EventHandler(Proc_Exited);
        }
        else
          TimeWaitExit = 0;
        System.Windows.Forms.Application.DoEvents();
        // запускаем приложение
        Proc.Start();
      }
      catch (Exception e) {
        // произошло исключение при запуске приложения
        // перегенирируем исключение
        throw new Exception(e.Message);
      }
      // ждем заданное время завершения работы приложения
      if (WaitExit) {
        const int SLEEP_AMOUNT = 200;
        while (!EventHandled) {
          System.Windows.Forms.Application.DoEvents();
          ElapsedTime += SLEEP_AMOUNT;
          if (ElapsedTime > TimeWaitExit)
            break;
          Thread.Sleep(SLEEP_AMOUNT);
        }
        if (!EventHandled) {
          // убиваем процесс приложения, за заданное время ожидания
          // он не завершился (считаем, что он "завис")
          Proc.Kill();
          return false;
        }
        else {
          // определяем код завершения приложения
          if (Proc.ExitCode == 0) {
            Proc.Close();
            return true;
          }
          else {
            Proc.Close();
            return false;
          }
        }
      }
      else {
        Proc.Close();
        return true;
      }
    }

    //*** End of Run ***********

    //*******************************
    //* Function: Proc_Exited
    //* Notes: Обработчик завершения процесса
    //*

    private void Proc_Exited (object sender, System.EventArgs e) {
      EventHandled = true;
    }

    //*** End of Proc_Exited ***********

    //*******************************
    //* Function: RunAlfa
    //* Notes: Запуск Alfa процесса
    //*

    public string RunAlfa (string Arg, string WorkDir, int TimeWaitExit) {
      // полное имя Alfa
      string FileName = "";
      // текущее время ожидание завершения процесса
      ElapsedTime = 0;

      // получить полное имя Alfa
      try {
        FileName = App.GetPath("Alfa") + @"\Alfa.exe";
      }
      catch (Exception e) {
        return e.Message;
      }
      RegistryKey RK = RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine, RegistryView.Registry64);
      RK = RK.OpenSubKey(@"SOFTWARE\UAF\Alfa");
      if (RK == null)
        throw new Exception("Ошибка, Alfa не установлена на компьютере!");
      FileName = (string)RK.GetValue("Name");
      if (FileName == "" || FileName == null)
        return "Ошибка, Alfa не установлена на компьютере!";
      // устанавливаем имя запускаемого приложения
      Proc.StartInfo.FileName = FileName;
      // проверяем аргументы командной строки запуска программы Alfa
      if ((Arg == null) || (Arg == ""))
        return "Ошибка, не указана программа для запуска!";
      else {
        string[] Param;
        try {
          Param = ParseStr(Arg);
        }
        catch (Exception e) {
          return e.Message;
        }
        string Ext = Path.GetExtension(Param[1]).ToLower();
        if (Ext != ".alf" && Ext != ".alfa" && Ext != ".alfc")
          return "Ошибка, неверное расширение файла программы!";
        if (!File.Exists(Param[1]))
          return ("Ошибка. Файл программы: " + Param[1] + " - не существует!");
      }
      // устанавливаем аргументы командной строки
      Proc.StartInfo.Arguments = Arg;
      // устанавливаем рабочий каталог
      if ((WorkDir != null) && (WorkDir != ""))
        Proc.StartInfo.WorkingDirectory = WorkDir;
      // устананавливаем время ожидания завершения приложения
      if (TimeWaitExit == 0)
        TimeWaitExit = 30000;
      else
        TimeWaitExit = TimeWaitExit * 1000;
      try {
        System.Windows.Forms.Application.DoEvents();
        // запускаем приложение
        Proc.Start();
        // ожидание завершения процесса
        do {
          System.Windows.Forms.Application.DoEvents();
          if (!Proc.HasExited) {
            ElapsedTime += SLEEP_AMOUNT;
            if (ElapsedTime > TimeWaitExit) {
              // убиваем процесс приложения, за заданное время ожидания
              // он не завершился (считаем, что он "завис")
              Proc.Kill();
              Thread.Sleep(SLEEP_AMOUNT);
              return ("Тайм-аут, процесс принудительно завершен!");
            }
          }
          else
            break;
        } while (!Proc.WaitForExit(SLEEP_AMOUNT));
        try {
          Thread.Sleep(SLEEP_AMOUNT);
        }
        catch (Exception e) {
          return e.Message;
        }
        return "";
      }
      catch (Exception e) {
        // произошло исключение при запуске приложения
        return e.Message;
      }
      finally {
        if (Proc != null)
          Proc.Close();
      }
    }

    //*** End of RunAlfa ***********

    //*******************************
    //* Function: RunAlfaEx
    //* Notes: Запуск Alfa процесса
    //*

    public bool RunAlfaEx (string Arg, string WorkDir,
                           int TimeWaitExit, bool Minimized) {
      // полное имя Alfa
      string FileName = "";
      // текущее время ожидание завершения процесса
      ElapsedTime = 0;
      bool WaitExit = true;
      const int SLEEP_AMOUNT = 100;
      try {
        // получить полное имя Alfa
        FileName = App.GetPath("Alfa") + @"\Alfa.exe";
        // устанавливаем имя запускаемого приложения
        Proc.StartInfo.FileName = FileName;
        // проверяем аргументы командной строки запуска программы Alfa
        if ((Arg == null) || (Arg == ""))
          throw new Exception("Ошибка, не указана программа для запуска!");
        else {
          string[] Param;
          Param = ParseStr(Arg);
          string Ext = Path.GetExtension(Param[0]).ToLower();
          if (Ext != ".alf" && Ext != ".alfa" && Ext != ".alfc")
            throw new Exception("Ошибка, неверное расширение программы!");
          if (!File.Exists(Param[0]))
            throw new Exception("Ошибка. Файл: " + Param[0] + " - не существует!");
        }
        // устанавливаем аргументы командной строки
        Proc.StartInfo.Arguments = Arg;
        // устанавливаем, что запускаемое приложение минимизирует окно
        if (Minimized)
          Proc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
        // устанавливаем рабочий каталог
        if ((WorkDir != null) && (WorkDir != ""))
          Proc.StartInfo.WorkingDirectory = WorkDir;
        // устананавливаем время ожидания завершения приложения
        if (TimeWaitExit == 0)
          WaitExit = false;
        else {
          TimeWaitExit = TimeWaitExit * 1000;
          if (EventExit != null) {
            Proc.Exited += new EventHandler(EventExit);
            Proc.EnableRaisingEvents = true;
          }
        }
        System.Windows.Forms.Application.DoEvents();
        // запускаем приложение
        Proc.Start();
        // ждем заданное время завершения работы приложения
        if (WaitExit) {
          // ожидание завершения процесса
          do {
            System.Windows.Forms.Application.DoEvents();
            if (!Proc.HasExited) {
              ElapsedTime += SLEEP_AMOUNT;
              if (ElapsedTime > TimeWaitExit) {
                // убиваем процесс приложения, за заданное время ожидания
                // он не завершился (считаем, что он "завис")
                Proc.Kill();
                throw new Exception("тайм-аут при выполнении!");
              }
            }
            else
              break;
          } while (!Proc.WaitForExit(SLEEP_AMOUNT));
          Proc.Close();
          return false;
        }
        return true;
      }
      catch (Exception e) {
        // произошло исключение при запуске приложения
        // перегенирируем исключение
        throw new Exception(e.Message);
      }
      finally {
        if (EventExit != null) {
          Proc.Exited -= new EventHandler(EventExit);
          Proc.EnableRaisingEvents = false;
        }
      }
    }

    //*** End of RunAlfaEx ***********

  }

  public static class App {

    // определить путь к интерпретатору Alfa
    private static string GetPathAlfa () {
      string PathAlfa = "";
      string NameIni = Application.StartupPath + @"\ClientSDT.ini";
      if (File.Exists(NameIni)) {
        IniFile Ini = new IniFile(NameIni);
        PathAlfa = Ini.ReadString("Path", "Alfa", "");
        if (!File.Exists(PathAlfa + @"\Alfa.exe"))
          return "";
      }
      return PathAlfa;
    }

    // определить путь к приложению
    public static string GetPath (string NameApp) {
      string FileName = "";

      // "ClientSDT"        
      if (NameApp.ToLower() ==
          Path.GetFileNameWithoutExtension(Application.ExecutablePath).ToLower()) {
        // проверяем наличие
        FileName = GetPathAlfa();
        if (FileName != "")
          return Application.StartupPath;
      }
      // "Alfa"
      if (NameApp == "Alfa") {
        FileName = GetPathAlfa();
        if (FileName != "")
          return FileName;
      }
      RegistryKey RK = RegistryKey.OpenBaseKey(
        RegistryHive.LocalMachine, RegistryView.Registry64);
      RK = RK.OpenSubKey(@"SOFTWARE\UAF\" + NameApp);
      if (RK == null)
        throw new Exception("Ошибка, программа " + NameApp +
          " не установлена на компьютере!");
      FileName = (string)RK.GetValue("Name");
      if (FileName == "" || FileName == null)
        throw new Exception("Ошибка, программа " + NameApp +
          " не установлена на компьютере!");
      return Path.GetDirectoryName(FileName);
    }

    public static string GetDateTime () {
      return ("Дата: " + DateTime.Now.ToString("dd.MM.yyyy") +
              " Время: " + DateTime.Now.ToString("HH:mm:ss"));
    }

    public static string[] MatchReg (string S, string Pattern, bool FullForm) {
      int I = 0;
      string[] Res;

      bool Flag = FullForm;
      Regex R = new Regex(Pattern);
      MatchCollection M = R.Matches(S);
      Res = new string[M.Count];
      if (Flag) {
        foreach (Match match in M) {
          Res[I] = match.Value + "|" + Convert.ToString(match.Index);
          I++;
        }
      }
      else
        foreach (Match match in M) {
          Res[I] = match.Value;
          I++;
        }
      return Res;
    }
  }

}


